package shortcircuit.graphics;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;

import shortcircuit.Display;
import shortcircuit.GameEngine;
import shortcircuit.basics.*;
import shortcircuit.basics.powerup.Null;
import shortcircuit.engine.InstructionsEngine;

/**
 * Holds all of the graphical elements related to the gaming interface of this
 * project.  The original graphics are retrieved from an instance of EnvironmentPane
 * while additional graphics such as walls and background will be added to the 
 * display.  All of these graphical elements are then converted into a JComponent
 * that can be easily manipulated and translated in JPanels.
 * 
 * @author Kevin Lewi
 */
public class GameScreen extends JPanel
{
	private JPanel tempPanel;
	public Point cursorPoint;
	private ArrayList<ArrayList<Cell>> allCompletePaths;
	public final int ENVIRONMENT_X_OFFSET = 20, ENVIRONMENT_Y_OFFSET = 20, 
	RECTANGLE_CLEARING_CONSTANT = 17;
	private GameEngine _Engine;
	private EnvironmentPane environmentPane;
	private Environment _Environment;
	public boolean drawingPointPath, beginFadingAnimation, readyToClear;
	private ArrayList<ArrayList<Point>> eliminatablePoints;
	private ArrayList<Rectangle> clearRectangles;
	private final int TEMP_Y_OFFSET = 93; //should be fixed, used to make displaying of path correct for now
	Graphics offGraphics;
	public Image offImage;
	public Image draggedPowerUpImage;
	ImageIcon theIcon;
	private int pointPathCounter, fadingAnimationCounter, clearingOffset;
	private final MouseListener theMouseListener = new MouseInput(this, "new");
	private final MouseMotionListener theMouseMotionListener = new MouseInput();
	private boolean isClickable = true;
	private int transparentBlockColumn;
	private Image transparentBlockImage;

	/**
	 * Initializes the graphics and images related to the display by using the
	 * EnvironmentPane passed into this constructor.
	 * 
	 * @param theEnvironmentPane The EnvironmentPane to be displayed in the screen
	 * @author Kevin Lewi
	 */
	public GameScreen(GameEngine theEngine)
	{
		cursorPoint = new Point();
		tempPanel = new JPanel();
		add(tempPanel);
		readyToClear = true;
		drawingPointPath = false;
		beginFadingAnimation = false;
		_Engine = theEngine;
		environmentPane = theEngine.getEnvironmentPane();
		_Environment = environmentPane.getEnvironment();

//		add(MainMenuScreen.bgPanel);
		setSize(_Environment.getGrid().length*40+40,_Environment.getGrid()[0].length*40+20);
		repaint();

	}

	public void addListeners()
	{
		if(this.getMouseListeners().length == 0)
		{
			addMouseListener(theMouseListener);
			addMouseMotionListener(theMouseMotionListener);
		}
	}

	public void removeListeners()
	{
		removeMouseListener(theMouseListener);
		removeMouseMotionListener(theMouseMotionListener);
	}

	public void drawPointPath(ArrayList<ArrayList<Point>> theEliminatablePoints)
	{
		drawingPointPath = true;
		eliminatablePoints = theEliminatablePoints;
	}

	public void setClearRectangles(ArrayList<Rectangle> clearRectangles)
	{
		this.clearRectangles = clearRectangles;
	}

	public void beginFadingAnimation(ArrayList<ArrayList<Cell>> thePaths)
	{
		allCompletePaths = thePaths;
	}

	/**
	 * Returns the EnvironmentPane of the GameScreen
	 * 
	 * @return The EnvironmentPane of the GameScreen
	 * @author Kevin Lewi
	 */
	public EnvironmentPane getEnvironmentPane()
	{
		return environmentPane;
	}

	public void setTransparentBlock(Cell selectedCell, int selectedColumn)
	{
		transparentBlockImage = selectedCell.getBlock().getTheImage();
		transparentBlockColumn = selectedColumn;
	}
	
	public void drawTransparentBlock()
	{
		Graphics2D g2D = (Graphics2D) offGraphics;
		boolean prevHadBlock = true;
		boolean currentHasBlock = false;
		for(int y = 0; y < _Environment.getGrid()[transparentBlockColumn].length; y++)
		{
			prevHadBlock = currentHasBlock;
			if(_Environment.getCell(transparentBlockColumn, y).hasBlock())
				currentHasBlock = true;
			if((!currentHasBlock && y == 0) || (!currentHasBlock && prevHadBlock))
			{
				g2D.drawImage(Display.titleImage, 40, 40, this);
				/*
				float[] scales = {1, 1, 1, 0.5f};
			    float[] offsets = new float[4];
				RescaleOp rop = new RescaleOp(scales, offsets, null);
				BufferedImage bi = new BufferedImage(40, 40, BufferedImage.TYPE_INT_ARGB);
				bi.getGraphics().drawImage(transparentBlockImage, 40, 40, null);
				g2D.drawImage(bi, rop, 40 * transparentBlockColumn, 40 * (y + 1));*/
				break;
			}
		}
	}
	
	/**
	 * Draws all of the elements related to the purely graphical aspects of
	 * the game and converts them into an easy-to-manipulate ImageIcon and adds
	 * this icon to the JPanel.
	 * 
	 * @author Kevin Lewi
	 */
	public void paint(Graphics g)
	{
		if(offGraphics == null || getWidth() != offImage.getWidth(null) || getHeight() != offImage.getHeight(null))
		{
			offImage = createImage(getWidth(), getHeight());
			offGraphics = offImage.getGraphics();
		}

		Graphics2D g2D = (Graphics2D) offGraphics;
		Composite origComposite = g2D.getComposite();
		g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
		g2D.fillRect(0, 0, 500, 500);
		g2D.setComposite(origComposite);


		g2D.setBackground(new Color(0, 0, 0, 0));
		g2D.clearRect(0, 0, 500, 500);
		offGraphics.setColor(new Color(255, 235, 245, 0));
		offGraphics.fillRect(0, 0, 200, 400);

		offGraphics.translate(ENVIRONMENT_X_OFFSET, getHeight() - (ENVIRONMENT_Y_OFFSET + 40 * environmentPane.getEnvironment().getGrid()[0].length));

		offGraphics.translate(-20, 0);
		offGraphics.drawImage(MainMenuScreen.currentWallClip.getImage(), 0, 0, this);
		offGraphics.translate(20, 0);
		environmentPane.getGraphics(offGraphics);

		offGraphics.translate(200, 400);
		offGraphics.drawImage(MainMenuScreen.currentWallClip.getImage(), 0, 0, this);
		offGraphics.translate(-200, -400);

		offGraphics.translate(-ENVIRONMENT_X_OFFSET, -getHeight() + (ENVIRONMENT_Y_OFFSET + 2 * 40 * environmentPane.getEnvironment().getGrid()[0].length));

		if(draggedPowerUpImage != null)
			offGraphics.drawImage(draggedPowerUpImage, cursorPoint.x - 20, cursorPoint.y - 20, this);

		float[] scales = {0.0f, 0.0f, 0.0f, 0.0f};
		float[] offsets = new float[4];

		/*
        RescaleOp rop = new RescaleOp(scales, offsets, null);
		BufferedImage bi = new BufferedImage(offImage.getWidth(null), offImage.getHeight(null), BufferedImage.TYPE_INT_ARGB);
		bi.getGraphics().drawImage(offImage, 0, 0, null);
		if(offImage != null)
			g2D.drawImage(bi, rop, 0, 0);
		 */
		g.drawImage(offImage, 0, 0, this);

		//theIcon = new ImageIcon(offImage);
		//tempPanel.removeAll();
		//tempPanel.add(new JLabel(theIcon));
	}


	public void passTransparentObjects(Cell theCell, int theCol)
	{
		environmentPane.getEnvironment().setTransparentCell(theCell, theCol);
	}

	public void setClickable(boolean theBoolean){
		isClickable = theBoolean;
	}
	
	/**
	 * Controls the mouse clicking and moving and sets which block is
	 * selected by the user.
	 * 
	 * @author Kevin Lewi
	 */
	public class MouseInput extends MouseAdapter implements MouseMotionListener
	{
		GameScreen gameScreen;
		Cell selectedCell;
		protected Element currentSelectedElement, previousSelectedElement,
		currentElement, previousElement;
		boolean isBlockTransitioning;

		/**
		 * Initializes all class-scope variables to be used in this listener.
		 * 
		 * @author Kevin Lewi
		 */
		public MouseInput(GameScreen theScreen, String s)
		{
			gameScreen = theScreen;
			selectedCell = new Cell();
			previousSelectedElement = null;
			currentSelectedElement = null;
			previousElement = null;
			currentElement = null;
			isBlockTransitioning = false;
		}

		public MouseInput() {}

		/**
		 * Converts a set of cursor coordinates into an element if the coordinate matches
		 * with an element.
		 * 
		 * @param cursorCoordinates The coordinates of the cursor
		 * @return The corresponding element of the cursor coordinates
		 * @author Kevin Lewi
		 */
		public Element convertToElement(Point cursorCoordinates)
		{
			Point cellCoord = new Point((int) Math.floor(((double) cursorCoordinates.x - (double) ENVIRONMENT_X_OFFSET) / 40.0), (int) Math.floor(((double) cursorCoordinates.y - (double) ENVIRONMENT_Y_OFFSET) / 40.0));
			return _Environment.getCell(cellCoord.x, cellCoord.y);
		}

		/**
		 * Determines which cell the user is hovering his mouse over and initiates the
		 * hover animation to the appropriate cell.  Ends by calling Display's paint method.
		 * 
		 * @author Kevin Lewi
		 */
		public void mouseMoved(MouseEvent e)
		{
			if(!(_Engine.popUpScreen.doneLoading && !_Engine.popUpScreen.beginFading))
				return;
			_Engine.setCellsHoverable();
			cursorPoint = e.getPoint();
			previousElement = currentElement;
			Point cursorCoordinates = new Point(e.getX(), Display.getFrames()[0].getHeight() - e.getY() - Display.TOOLBAR_OFFSET);

			currentElement = convertToElement(cursorCoordinates);
			if(currentElement != null && ((Cell) currentElement).isHoverable())
			{
				((Cell) currentElement).setHovered(true);
			}
			if((Cell) previousElement != (Cell) currentElement && previousElement != null)
				((Cell) previousElement).setHovered(false);
			//if(currentSelectedElement != null)
			//	setTransparentBlock(selectedCell, _Environment.getLocation((Cell) currentElement).x);
		}

		public void mouseDragged(MouseEvent e)
		{
			cursorPoint = e.getPoint();
		}

		/**
		 * Deals with the selecting of elements based on mouse clicking and the appropriate
		 * actions if the element is a selectable cell.
		 * 
		 * @param e The MouseEvent passed into this method
		 * @author Kevin Lewi
		 */
		public void mousePressed(MouseEvent e)
		{
			if(isClickable){
				if(!(_Engine.popUpScreen.doneLoading && !_Engine.popUpScreen.beginFading))
					return;
				previousElement = currentElement;
				Point cursorCoordinates = new Point(e.getX(), Display.getFrames()[0].getHeight() - e.getY() - Display.TOOLBAR_OFFSET);
	
				_Engine.setCellsHoverable();
				currentElement = convertToElement(cursorCoordinates);
				if(currentElement != null && ((Cell) currentElement).isHoverable())
				{
					((Cell) currentElement).setHovered(true);
				}
				if((Cell) previousElement != (Cell) currentElement && previousElement != null)
					((Cell) previousElement).setHovered(false);
				
				//if(currentSelectedElement != null)
				//	setTransparentBlock(selectedCell, _Environment.getLocation((Cell) currentElement).x);
				
				Element tempElement = currentSelectedElement;
				previousSelectedElement = currentSelectedElement;
				if(currentElement != null)
					currentSelectedElement = true ? 
							currentElement : currentSelectedElement;
				Cell prevCell;
				try
				{
					prevCell = previousSelectedElement.getClass().getName().equals(Cell.class.getName()) ? 
							(Cell) previousSelectedElement : null;
				}
				catch(NullPointerException f) { prevCell = null; }
				Cell currentCell = currentSelectedElement.getClass().getName().equals(Cell.class.getName()) ? (Cell) currentSelectedElement : null;
	
				if(e.getButton() == MouseEvent.BUTTON3 && draggedPowerUpImage != null)
				{
					draggedPowerUpImage = null;
					_Engine.statsScreen.labelSelected = -1;
					_Engine.statsScreen.update();
				}
				else if(draggedPowerUpImage != null)
					executeBombPowerUp(currentCell);
				else if(e.getButton() == MouseEvent.BUTTON3 && isBlockTransitioning)
					deselectCell(currentCell, prevCell);
				else if(e.getButton() == MouseEvent.BUTTON1 && currentCell.isHovered() && currentCell.hasBlock() && !isBlockTransitioning)
					selectCell(currentCell);
				else if(!currentCell.hasBlock() && _Environment.getCell(_Environment.getLocation(currentCell).x, _Environment.getLocation(currentCell).y).isHoverable() &&
						e.getButton() == MouseEvent.BUTTON1 && prevCell != null && 
						!(_Environment.getLocation(prevCell).x == _Environment.getLocation(currentCell).x && _Environment.getLocation(prevCell).y + 1 == _Environment.getLocation(currentCell).y) && 
						isBlockTransitioning){
					moveBlock(currentCell, prevCell);
					for(int y=_Environment.getLocation(prevCell).y;y<_Environment.getGrid()[0].length;y++){
						if(_Environment.getGrid()[_Environment.getLocation(prevCell).x][y].hasBlock())
						{
							environmentPane.addBlock(_Environment.getCell(_Environment.getLocation(prevCell).x,y).getBlock(),new Point(_Environment.getLocation(prevCell).x,y+1));
							_Environment.clearCell(_Environment.getCell(_Environment.getLocation(prevCell).x,y));
						}
					}
				}
				else if(currentCell.hasBlock() && currentCell.isHoverable() &&
						e.getButton() == MouseEvent.BUTTON1 && prevCell != null && isBlockTransitioning){
					moveBlock(currentCell, prevCell);
				}
	
				else
				{
					currentSelectedElement = tempElement;
				}
				gameScreen.repaint();
			}
		}

		void executeBombPowerUp(Cell currentCell)
		{
			selectedCell = null;
			try
			{
				((InstructionsEngine) _Engine).instructionsScreen.powerUpSelected = false;
			}
			catch(Exception e){}
				
			draggedPowerUpImage = null;
				
			_Engine.completePathAddition = new ArrayList<Cell>();
				
			ArrayList<Cell> deletableCollection = new ArrayList<Cell>();
			deletableCollection.add(null);
			Environment env = _Engine.getEnvironmentPane().getEnvironment();
			Point currentCellLoc = env.getLocation(currentCell);
			_Engine.completePathAddition.add(env.getCell(currentCellLoc.x - 1, currentCellLoc.y + 1));
			_Engine.completePathAddition.add(env.getCell(currentCellLoc.x, currentCellLoc.y + 1));
			_Engine.completePathAddition.add(env.getCell(currentCellLoc.x + 1, currentCellLoc.y + 1));
			_Engine.completePathAddition.add(env.getCell(currentCellLoc.x - 1, currentCellLoc.y));
			_Engine.completePathAddition.add(env.getCell(currentCellLoc.x, currentCellLoc.y));
			_Engine.completePathAddition.add(env.getCell(currentCellLoc.x + 1, currentCellLoc.y));
			_Engine.completePathAddition.add(env.getCell(currentCellLoc.x - 1, currentCellLoc.y - 1));
			_Engine.completePathAddition.add(env.getCell(currentCellLoc.x, currentCellLoc.y - 1));
			_Engine.completePathAddition.add(env.getCell(currentCellLoc.x + 1, currentCellLoc.y - 1));
			_Engine.completePathAddition.removeAll(deletableCollection);
			_Engine.powerUps.remove(0);
			_Engine.powerUps.add(new Null());
			try{
			_Engine.statsScreen.labelSelected = -1;
			}
			catch(Exception e){}
			_Engine.movesMade++;
			_Engine.setCellsHoverable();
		}
		
		void deselectCell(Cell currentCell, Cell prevCell)
		{
			isBlockTransitioning = false;
			if(prevCell != null)
			{
				if(prevCell == currentCell)
				{
					currentCell.setSelected(false);
					currentCell.setBlock(selectedCell.getBlock());
				}
				else
				{
					prevCell.setSelected(false);
					prevCell.setBlock(selectedCell.getBlock());
				}
			}
			currentSelectedElement = null;
		}

		void selectCell(Cell currentCell)
		{
			isBlockTransitioning = true;
			currentCell.setSelected(true);
			selectedCell.setBlock(currentCell.getBlock());
		}

		void moveBlock(Cell currentCell, Cell prevCell)
		{
			isBlockTransitioning = false;
			if(prevCell != null)
				prevCell.setSelected(false);
			if(currentCell == prevCell)
			{
				prevCell.setBlock(selectedCell.getBlock());
			}
			else
			{
				Block tempBlock = currentCell.getBlock();
				currentCell.setBlock(selectedCell.getBlock());
				prevCell.setBlock(tempBlock);
			}
			for(int x = 0; x < _Environment.getGrid().length; x++)
				for(int y = 0; y < _Environment.getGrid()[x].length; y++)
					_Environment.getCell(x, y).setSelected(false);

			selectedCell.removeBlock();
			environmentPane.changeTopBlocks();
			if(currentCell != prevCell)
				_Engine.movesMade++;
			_Engine.setCellsHoverable();
			_Engine.update();
			if(_Engine.statsScreen != null)
				_Engine.statsScreen.update();
		}
	}
}
